﻿using System;

using octalforty.Componento.Core.ObjectModel;

using System.Linq;

namespace octalforty.Componento.Core.ApplicationServices
{
    public class DependencyGraphManagerService
    {
        public PackageRepositoryBrowserService PackageRepositoryBrowser
        { get; set; }

        /// <summary>
        /// Builds a <see cref="DependencyGraph"/> for all packages starting from <paramref name="rootManifest"/> in
        /// repositories identified by <paramref name="repositoryUris"/>.
        /// </summary>
        /// <param name="rootManifest"></param>
        /// <param name="repositoryUris"></param>
        /// <returns></returns>
        public virtual DependencyGraph BuildDepedencyGraph(PackageManifest rootManifest, params Uri[] repositoryUris)
        {
            var depedencyGraph = new DependencyGraph();
            BuildDependencyGraphNode(rootManifest, null, depedencyGraph, repositoryUris);

            return depedencyGraph;
        }

        private DependencyGraphNode BuildDependencyGraphNode(PackageManifest package, PackageManifest parentPackage, DependencyGraph dependencyGraph, params Uri[] repositoryUris)
        {
            var packageNode = dependencyGraph.GetNodeFor(package.Name, package.Version);

            if(parentPackage != null && packageNode != null)
            {
                var parentPackageNode = dependencyGraph.GetNodeFor(parentPackage.Name, parentPackage.Version);
                packageNode.DependencyFor.Add(parentPackageNode);
            } // if

            if(packageNode != null) return packageNode;

            packageNode = new DependencyGraphNode(package);
            dependencyGraph.AddNode(packageNode);

            if(parentPackage != null)
            {
                var parentPackageNode = dependencyGraph.GetNodeFor(parentPackage.Name, parentPackage.Version);
                packageNode.DependencyFor.Add(parentPackageNode);
            } // if

            if(package.Dependencies != null)
            {
                foreach(var dependency in package.Dependencies)
                {
                    var packageSpecification = new PackageSpecification(dependency.Version, null, null);
                    var dependencyManifest = 
                        repositoryUris.
                            Select(ru => PackageRepositoryBrowser.GetPackageManifest(ru, dependency.Name, packageSpecification)).
                            FirstOrDefault(pm => pm != null);

                    if(dependencyManifest == null) continue;

                    var dependencyNode = BuildDependencyGraphNode(dependencyManifest, package, dependencyGraph, repositoryUris);

                    packageNode.DependentOn.Add(dependencyNode);
                } // foreach
            } // if

            return packageNode;
        }
    }
}
